package com.ethan.blog.controlller.user;

import cn.hutool.core.bean.BeanUtil;
import com.ethan.blog.common.constant.MessageConstant;
import com.ethan.blog.common.constant.StatusCodeConstant;
import com.ethan.blog.common.exception.PasswordErrorException;
import com.ethan.blog.common.properties.JwtProperties;
import com.ethan.blog.common.result.Result;
import com.ethan.blog.common.utils.JwtUtil;
import com.ethan.blog.common.utils.ThreadLocalUtil;
import com.ethan.blog.pojo.dto.user.UserDTO;
import com.ethan.blog.pojo.dto.user.UserRegisterDTO;
import com.ethan.blog.pojo.entity.User;
import com.ethan.blog.pojo.vo.user.UserVO;
import com.ethan.blog.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.Pattern;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static cn.hutool.core.date.DateTime.now;

/**
 * 用户表;(user)表控制层
 *
 * @author : Ethan
 * @date : 2024-5-24
 */
@Slf4j
@Tag(name = "userController", description = "用户表对象功能接口")
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    public JwtProperties jwtProperties;

    @Autowired
    private UserService userService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Operation(description = "测试接口")
    @PostMapping("/hello")
    public String hello() {
        return "Hello! Welcome to EthanBlog.  The time now is" + now();
    }

    /**
     * todo: 用户身份二次验证
     */
    public Boolean authenticateUser(Long id) {
        log.info("正在二次验证用户身份...");
        log.info("用户身份验证成功!");
        return true;
    }

    /**
     * 用户修改密码
     *
     * @param oldPassword
     * @param newPassword
     * @return Result
     */
    @Operation(description = "用户更改密码")
    @PatchMapping("/updatePwd")
    public Result updatePwd(@Pattern(regexp = StatusCodeConstant.LOGIN_PASSWORD_PATTERN, message = MessageConstant.ACCOUNT_INVALID) String oldPassword,
                            @Pattern(regexp = StatusCodeConstant.LOGIN_PASSWORD_PATTERN, message = MessageConstant.ACCOUNT_INVALID) String newPassword) {
        Map<String, Object> claims = ThreadLocalUtil.get();
        Long id = ((Integer) claims.get("id")).longValue();
        Result result = null;
        // 密码不能相同
        if (oldPassword.equals(newPassword)) {
            log.info("新密码与旧密码相同");
            result = Result.error(MessageConstant.PASSWORD_EQUAL_OLD);
        } else if (authenticateUser(id)) {// 用户二次验证
            User user = userService.getById(id);
            // todo: md5加密
            if (oldPassword.equals(user.getPassword())) {
                boolean isUpdate = userService.updatePwd(id, newPassword);
                if (isUpdate) {
                    log.info("密码修改成功");
                    // 调用登出功能
                    result = logout();
                } else {
                    result = Result.error(MessageConstant.PASSWORD_EDIT_FAILED);
                }
            } else {
                log.info(MessageConstant.OLD_PASSWORD_ERROR);
                result = Result.error(MessageConstant.OLD_PASSWORD_ERROR);
            }
        }
        return result;
    }

    /**
     * 用户注销，逻辑删除
     *
     * @return Result.success()
     */
    @Operation(description = "用户注销，逻辑删除，需要权限验证")
    @DeleteMapping("/delete")
    public Result delete() {
        Map<String, Object> claims = ThreadLocalUtil.get();
        Long id = ((Integer) claims.get("id")).longValue();
        // todo: 权限验证
        if (!authenticateUser(id)) {
            log.info("用户身份验证成功");
        }
        Boolean isDeleted = userService.delete(id);
        if (isDeleted) {
            log.info("用户 {} 成功逻辑删除", id);
        } else {
            log.info("用户 {} 删除失败", id);
        }
        return Result.success();
    }

    /**
     * 获取用户信息
     *
     * @return 返回出密码外的所有信息
     */
    @Operation(description = "获取用户信息")
    @GetMapping("/info")
    public Result<UserVO> getUserInfo() {
        Map<String, Object> claims = ThreadLocalUtil.get();
        Long id = ((Integer) claims.get("id")).longValue();
        User user = userService.getByAll(User::getId, id);
        return Result.success(BeanUtil.copyProperties(user, UserVO.class));
    }

    /**
     * 用户凳出
     *
     * @return Result.success()
     */
    @Operation(description = "用户退出登录")
    @PostMapping("/logout")
    public Result logout() {
        Map<String, Object> claims = ThreadLocalUtil.get();
        // todo: 字符串优化
        String token = (String) claims.get("token");
        String account = (String) claims.get("account");
        // 删除redis中的token
        Boolean deleted = stringRedisTemplate.delete(token);
        if (deleted) {
            log.info("redis' token: {} 成功删除", token);
        } else {
            log.info("redis' token: {} 删除失败", token);
        }
        // 删除线程中的token
        ThreadLocalUtil.remove();
        log.info("ThreadLocal' token: {} 成功删除", token);
        log.info("用户 {} 于 {} 时退出登录", account, now());
        return Result.success();
    }

    /**
     * 用户信息更新
     *
     * @param userDTO
     * @return
     */
    @Operation(description = "用户信息更新")
    @PostMapping("/update")
    public Result update(@RequestBody @Validated UserDTO userDTO) {
        // 属性转换
        User user = BeanUtil.copyProperties(userDTO, User.class);
        // 获取用户账号
        Map<String, Object> claims = ThreadLocalUtil.get();
        Long id = ((Integer) claims.get("id")).longValue();
        user.setId(id);
        String account = (String) claims.get("account");
        user.setAccount(account);
        boolean isUpdateUser = userService.updateUser(user);
        if (isUpdateUser) {
            log.info("用户 {} 于 {} 时更新信息成功", user.getId(), now());
        } else {
            log.info("用户 {} 于 {} 时更新信息失败", user.getId(), now());
        }
        return Result.success();
    }

    /**
     * 用户注册
     *
     * @param userRegisterDTO
     * @return
     */
    @Operation(description = "用户注册")
    @PostMapping("/register")
    public Result register(@RequestBody @Validated UserRegisterDTO userRegisterDTO) {
        String account = userRegisterDTO.getAccount();
        String password = userRegisterDTO.getPassword();

        Result result = null;

        User byAccount = userService.getByAll(User::getAccount, account);
        if (byAccount != null) {
            log.info("账号已存在: {}", byAccount);
            result = Result.error(MessageConstant.ALREADY_EXISTS);
        } else {
            User user = new User();
            BeanUtils.copyProperties(userRegisterDTO, user);
            // todo: 密码加密
            user.setPassword(password);
            Boolean insertUser = userService.insertUser(user);
            if (insertUser) {
                log.info("用户 {} 于 {} 时注册成功", user.getId(), now());
            } else {
                log.info("用户于 {} 时注册失败", now());
            }
            result = Result.success();
        }

        return result;
    }

    /**
     * 用户登录（多种不同登录方式）
     *
     * @param username
     * @param password
     * @return 包含UserVO的结果集
     */
    @Operation(description = "用户登录")
    @PostMapping("/login")
    public Result<UserVO> login(@RequestParam @Pattern(regexp = StatusCodeConstant.LOGIN_MULTI_TYPE_VALIDATION_PATTERN, message = MessageConstant.ACCOUNT_INVALID) String username,
                                @RequestParam @Pattern(regexp = StatusCodeConstant.LOGIN_PASSWORD_PATTERN, message = MessageConstant.PASSWORD_INVALID) String password) {
        Result<UserVO> result = null;

        // 校验账号合法性
        String accountOrPhoneNumberOrEmail = username;
        User user = userService.getByAll(accountOrPhoneNumberOrEmail);

        if (user == null) {
            log.info("用户不存在");
            return Result.error(MessageConstant.ACCOUNT_NOT_FOUND);
        }

        // TODO: md5加密
        String md5Password = password;
//        String userPassword = user.getPassword();
//        log.info("用户输入的密码: " + md5Password + " 数据库中的密码: " + userPassword);
        if (md5Password.equals(user.getPassword())) {

            if (user.getDeleted() == StatusCodeConstant.IS_DELETE_YES) {
                log.info("用户已被删除(被隐藏)");
                return Result.error(MessageConstant.ACCOUNT_LOCKED);
            }

            log.info("登录成功");
            // 设置token
            Map<String, Object> claims = new HashMap<>();
            claims.put("id", user.getId());
            claims.put("account", user.getAccount());

            String token = JwtUtil.generateToken(claims);
            log.info("生成的token: " + token);
            UserVO userVO = UserVO.builder()
                    .token(token)
                    .build();
            BeanUtils.copyProperties(user, userVO);
            // token存入redis
            ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
            // keypoint：这里需要设置TimeUnit.MILLISECONDS，不然会遇到ERR string exceeds maximum allowed size (512MB)
            operations.set(token, token, jwtProperties.getExpireTime(), TimeUnit.MILLISECONDS);
            result = Result.success(MessageConstant.LOGIN_SUCCESS, userVO);
        } else {
            log.info("密码错误");
//            result = Result.error(MessageConstant.PASSWORD_ERROR);
            throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
        }
        return result;
    }
}